This is version two of Drawing Bézier Curves. It moves forward from the
first version, which ended with successfully
rotating, translating, scaling and rendering a swatch. The swatch is the basic shape
of which everything else will be constructed.
Version one successfully manages the swatch. Now I need to manage letters made of
multiple swatches. The Hebrew letters used are made of semi-rectangular
blocks or swatches consisting of two quadratic Bézier curves sharing the same
endpoints. The letters needed for this project are chet, waw,
he, dalet, alef, and yod.
י א ד ה ו ח
Below are the two words that will recursively produce the desired final picture. I
am avoiding calling it art. These two words at the lowest level will be made from
letters, themselves made from one, two or three swatches. Tiny versions of these
words will be arranged to produce larger versions of the same words. These larger
words will again be arranged to produce a larger version of the same two words. This
is the plan.
The letters will look significantly different than those shown above as they are
all made from the swatches successfully created in version one.
After writing the swatch object the next challenge is rewriting the letter object in
light of a swatch object. Successfully getting the correct size, orientation and
location info to the swatch so it can render itself is front and center the crux of
the problem. At some point the letters will be created in order to makeup a word.
The location of the letter within the word will also need to be received by the
letter object and incorporated in the information sent to the swatches.
Once the letter object is rewritten each of the six letters must be defined. The
various functions currently written must work for multiple swatches making up a
single letter. Five new letters need to be defined: dalet, he,
aleph, waw & chet. The letters will need to store only
the swatches and their locations, orientations, and sizes. The relative sizes of
the swatches making up each letter are initially: yod - 1;
dalet - 2, 2; chet - 2, 2, 2; aleph - 1.5, 2.5, 1;
he - 1, 2, 2; & waw - 1, 2. Relative locations need to be the same
across letters, i.e. the yod needs to be moved up in the SVG window.
The letters will initially be made only of swatches. Two of the six letters currently
planned may need some graphics besides swatches. The aleph is often rendered
with a fine line connecting the upper right small swatch to the long central swatch.
The waw is usually represented with a wide solid line as its descending part
on the right side. Again I will first try imitating this with a swatch and hope for
the best.
Larger letters would only be needed to make templates for locating the words from
which it will be composed. Each letter will need to be "made" and then rotated and
translated to match the word's orientation and location. (Though all rotations begin
at the word level or higher.) Once things have been rotated and translated at these
two levels and then at the POP level, the final SVG will be generated.
Once we have the generic letters, we need to be able to render them as a unit, a word.
The letters will now need a location different from that used to make the generic
individual letter. A word object is needed for this level. Words will automatically
include the appropriate spacing between letters.
Finally, a picture object is required. This object is made from words, with each
word sized, oriented, and translated appropriately. The picture generated is, of
course, just one of the same words, but with no outline.
There is a final level in this plan. It is not clear if it needs its own object. This
level is a picture of a picture, a POP. Pictures are assembled to generate a higher
level POP that represents the same picture it is assembled from. An orientation,
position, and size is needed for each picture within the POP. These orientations and
locations are relatively the same as those used for assembling pictures from words.
That is why it is not clear if another object is needed.
This recursive level building is seen most clearly with the rendering process. Telling
a POP to render itself causes the POP to tell the pictures composing it to render
themselves. Each of these pictures will tell the words composing it to render
themselves. Each of these words will tell the letters they are made of to render
themselves. Finally, when carried to the ultimate extreme, each letter will tell its
component swatches to render themselves.
This raises the real conundrum with which I am now wrestling. How does one propagate
the various rotations and translations down through the various levels to finally
arrive at swatches that render themselves in the appropriate location and
orientation?
Task one is rewriting swatch as an object that can produce its own SVG. Each letter,
when told to produce its SVG, will tell its component swatch objects to write their
own SVG. The letters will hold instructions on how to orient and locate each of their
component swatches. My only concern with this approach is that by the end of the day
we will end up with thousands of swatch objects. Sticking with rendering at the level
of letters cuts that number by a factor of 2 1/3. We will start with swatch
objects.
The swatch object was defined. It contains the control point matrix for the two hard
coded Bézier curves making up the basic swatch. It has a getter to retrieve
the basicSwatch array. It also has a setter to modify this array if ever needed. The
swatch object has one further method to create its own SVG code. This method requires
three parameters: the degrees of rotation, the scale factor to size the swatch, and
the array of two points for translating in the x- and y-directions. The order of
events is rotating the basic swatch, scaling the rotated swatch, and translating the
rotated and scaled swatch. This method returns the SVG code for the two curves. It
does not include any opening or closing SVG.
Onto the new improved letter object. The letter object should have the following
properties: name, number of swatches, and array of swatch information. The swatch
information consists of rotation, size relative to the other swatches in this letter,
and location also relative to the other swatches in this letter. It will have methods
to produce its SVG code (or send the relevant info to the swatch objects), set its
properties, and get its properties. The method which produces the letter's SVG code
will take parameters that modify its swatch information array so it is drawn in the
correct location and at the correct size to fit in a word.
Swatches rotate around their center of mass by default. Letters will rotate around
their center of mass and words around their center of mass. When a word is told to
render itself it sends its location, orientation and size data to its letters. This
information has to be translated so the letters can use it in their own frames of
reference. The same has to happen when the letters send their info in their frame of
reference to the swatches. At this lowest level the info needs to be converted to the
swatch's frame of reference. As an example, rotating a letter with two swatches,
turns them as a unit. The two individual swatches experience both a rotation and a
translation. This is a matrix manipulation of some sort.
The rotatePt function was modified to accomplish rotation around any origin point.
It is now parallel to the rotateArr function. TransArr is already setup to shift the
entire swatch to the new CoM. It just requires that the x- and y-differences between
the two CoM's are passed as parameters for the translation. The swatch function,
makeSVG, needs to add the CoML as a parameter. If this parameter is
present, then the calculation is modified to handle this off CoM rotation.
I am struggling with how to rotate around an arbitrary point. It is already necessary
to translate the swatch array (or point) to the coordinate origin in order to perform
a rotation. To rotate around an arbitrary point, the swatch array must be translated
to the point, this point translated to the origin, the array (or point) rotated...
Not True!!! I have been rotating points around the center of mass. This is
not arbitrary, but the same formula applies. To rotate a point B around any arbitrary
point A use the following two equations for rotated B ≡ B'.
B'x = ((Bx - Ax)cosθ - (By - Ay)sinθ) + Ax
B'y = ((Bx - Ax)sinθ + (By - Ay)cosθ) + Ay
The basicSwatch object was modified to include a new property, ctrOfMass, and a
getter for ctrOfMass. This is of course the center of mass of the basic swatch and
may be used frequently. The createLetters() function needs to be modified next. This
requires modifying the letter{} object. The letter object needs to have a place to
store spacing before and after the letter. This varies with letter and needs to be
consistent as the letters are placed in words.
On further thought only spaceLeft was added as a property. The word object should
also have a spaceLeft property. I now need to define the swatches for each letter.
I need to work first with yod. It will be used to make sure I can actually
create a letter and render it to the screen.
I have written two different ways to fill the already initialized letters with data.
The first was letter by letter. Each letter has its own function. The characteristics
that define the letter's swatches are included in the function. I also wrote a very
simple function that will create any letter given the appropriate information. This
includes sending the characteristics of each swatch in an array of arrays. This is
probably the preferred method as it minimizes the number of functions. Also if the
process needs to be changed it can be done in only one spot.
Of course this immediately happened when I realized I had not included setting the
spaceLeft property.
Attempted the first code run through this morning. After fixing the obvious typos,
errors related to the various objects started to appear. Whether this is due to not
properly initializing the objects or to not using object prototypes is unclear. My
first attempt at fixing the issues is rearranging the code. Clicking the button will
call a small function that gets the input. This function will call the main function,
which will contain the swatch object at least.
The function calcOutput() called by hitting the "Render Output" button now does three
things. It gathers the input from the user, calls main(), and writes the returned SVG
to the browser window. The function main() has gathered a number of activities. the
swatch object now resides within main() where it can be used by all of the letter
objects. These are initialized next. Then the new letters are filled. The base letter
object sits outside of main(). The final major action of main is to render a letter.
It does this by looking at its first parameter, lett, which holds the desired letter.
This calls the letter object's method, createSVG(). Letter.createSVG() calls the
swatches createSVG() method. This method calls makeSVG(), returning the SVG for the
desired swatch. The SVG traverses back up the call hierarchy to calcOutput() and is
rendered on the screen.
The first error received says that spread requires that the iterable not be null or
undefined. It refers to the createLetter() function. This is where the letter's
swatches are added one-by-one to the new letter. I was creating the letters
incorrectly. The letter object was turned into a prototype. Initializing the letters
was done with new Letter(). The createLetter() function was skipped and the letters
were initialized with data as they were created.
Numerous bugs were fixed. They were mostly associated with using a point as a function
parameter. Sometimes an array was used and sometimes two scalars were used. The yod
is now printed to the screen, but not rotated appropriately. Unfortunately, all of
the letters render only the basic swatch. A small mistake here and there including
"/svg" added during swatch translation to SVG. So only the first swatch was
appearing on screen. Now I need to move the various swatches around so they look like
a letter.
Below the letters are represented as they now appear: yod, dalet, chet, aleph, he,
and waw. They definitely need some tweaking, but they are acceptable.